/*
 * Copyright (C) 2013 by KB3ZYT Rich Nash
 * 
 * Module modified after being inheritted from EA5HAV. The same license described below
 * is in effect.
 *
 * The below copyright is kept intact.
 *
 */

/* Credit to the trackuino project written by EA5HAV Javi
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "ax25.h"
#include "config.h"
#include "afsk_avr.h"
#include <stdint.h>
#include <Arduino.h>

// Module constants

// Module globals
static uint16_t crc;
static uint8_t ones_in_a_row;
static unsigned int packet_size; // size in bits of packets
static uint8_t *packet;
static unsigned int maxPacketSize;  // bytes

// Module functions
static void
update_crc(uint8_t a_bit) 
{
 	uint16_t xor_int;
    
	xor_int = crc ^ a_bit;				// XOR lsb of CRC with the latest bit
	crc >>= 1;									// Shift 16-bit CRC one bit to the right
    
	if (xor_int & 0x0001)					// If XOR result from above has lsb set
	{
		crc ^= 0x8408;							// Shift 16-bit CRC one bit to the right
	}
    
	return;

//    crc ^= a_bit;
//    if (crc & 1)
//        crc = (crc >> 1) ^ 0x8408;  // X-modem CRC poly
//    else
//        crc = crc >> 1;
}

static void
send_byte(uint8_t a_byte)
{
    uint8_t i = 0;
    while (i++ < 8) {
        uint8_t a_bit = a_byte & 1;
        a_byte >>= 1;
        update_crc(a_bit);
        if (a_bit) {
            // Next bit is a '1'
            if (packet_size >= maxPacketSize * 8)  // Prevent buffer overrun
                return;
            packet[packet_size >> 3] |= (1 << (packet_size & 7));
            packet_size++;
            if (++ones_in_a_row < 5) continue;
        }
        // Next bit is a '0' or a zero padding after 5 ones in a row
        if (packet_size >= maxPacketSize * 8)    // Prevent buffer overrun
            return;
        packet[packet_size >> 3] &= ~(1 << (packet_size & 7));
        packet_size++;
        ones_in_a_row = 0;
    }
}

// Exported functions
void
ax25_send_byte(uint8_t a_byte)
{
    // Wrap around send_byte, but prints debug info
    send_byte(a_byte);
#ifdef DEBUG_AX25
    Serial.print((char)a_byte);
#endif
}

void
ax25_send_flag()
{
    uint8_t flag = 0x7e;
    int i;
    for (i = 0; i < 8; i++, packet_size++) {
        if (packet_size >= maxPacketSize * 8)  // Prevent buffer overrun
            return;
        if ((flag >> i) & 1)
            packet[packet_size >> 3] |= (1 << (packet_size & 7));
        else
            packet[packet_size >> 3] &= ~(1 << (packet_size & 7));
    }
}

void
ax25_send_string(const char *string)
{
    int i;
    for (i = 0; string[i]; i++) {
        ax25_send_byte(string[i]);
    }
}

void ax25_initBuffer(uint8_t* buf, int bufSize)
{
    packet = buf;
    maxPacketSize = bufSize;
    packet_size = 0;
    ones_in_a_row = 0;
    crc = 0xffff;
}

void ax25_send_header(const struct s_address *addresses, int num_addresses)
{
    
    int i, j;
    
    // Send flags during TX_DELAY milliseconds (8 bit-flag = 8000/1200 ms)
    for (i = 0; i < TX_DELAY * 3 / 20; i++) {
        ax25_send_flag();
    }
    
    for (i = 0; i < num_addresses; i++) {
        // Transmit callsign
        for (j = 0; addresses[i].callsign[j]; j++)
            send_byte(addresses[i].callsign[j] << 1);
        // Transmit pad
        for ( ; j < 6; j++)
            send_byte(' ' << 1);
        // Transmit SSID. Termination signaled with last bit = 1
        if (i == num_addresses - 1)
            send_byte(('0' + addresses[i].ssid) << 1 | 1);
        else
            send_byte(('0' + addresses[i].ssid) << 1);
    }
    
    // Control field: 3 = APRS-UI frame
    send_byte(0x03);
    
    // Protocol ID: 0xf0 = no layer 3 data
    send_byte(0xf0);
    
#ifdef DEBUG_AX25
    // Print source callsign
    Serial.println();
    Serial.print('[');
    Serial.print(millis());
    Serial.print("] ");
    Serial.print(addresses[1].callsign);
    if (addresses[1].ssid) {
        Serial.print('-');
        Serial.print((unsigned int)addresses[1].ssid);
    }
    Serial.print('>');
    // Destination callsign
    Serial.print(addresses[0].callsign);
    if (addresses[0].ssid) {
        Serial.print('-');
        Serial.print((unsigned int)addresses[0].ssid);
    }
    for (i = 2; i < num_addresses; i++) {
        Serial.print(',');
        Serial.print(addresses[i].callsign);
        if (addresses[i].ssid) {
            Serial.print('-');
            Serial.print((unsigned int)addresses[i].ssid);
        }
    }
    Serial.print(':');
#endif
}

void 
ax25_send_footer()
{
    // Save the crc so that it can be treated it atomically
    uint16_t final_crc = crc;
    
    // Send the CRC
    send_byte(~(final_crc & 0xff));
    final_crc >>= 8;
    send_byte(~(final_crc & 0xff));
    
    // Signal the end of frame
    ax25_send_flag();
#ifdef DEBUG_AX25
    Serial.print("\nax25 bits: ");
    Serial.print(packet_size);
    Serial.print(" of ");
    Serial.println((maxPacketSize*8));
#endif
}

void
ax25_flush_frame()
{
    Serial.flush(); // Make sure alll Serial data (which is based on interrupts) is done before you start sending.
    
    // Key the transmitter and send the frame
    afsk_send(packet, packet_size);

    // OK, no more operations until this is done.
    afsk_start();
    while (afsk_busy()) ;
    Serial.print("\nax25 bits sent: ");
    Serial.println(afsk_packets_sent());
}
